Programming oriented around objects
"object" : A structure with both data and functions
Similar to a C or IDL struct, but with functions "bound" to them.
fobj = open('../README.md')
fobj.name
'../README.md'
fobj.readline()
'PyLunch Nova <sup>*</sup>\n'
print(fobj.tell())
fobj.readline()
print(fobj.tell())
26 52
Everything in Python is an object. (Even the classes themselves! Mind-bending...)
Objects hide details you don't care about (E.g., where am I in the file), and provide a unified view of what you do.
Objects make it easier for you to extend someone else's work without (E.g., domain-specific coordinates in astropy, your own point spread function in photutils).
MyClassName.__init__: A special name Python uses for the method called when a new object is instantiatedGo to https://github.com/spacetelescope/pylunch and clone it (if you know git). Then just find the Object Oriented Intro.ipynb file.
Go to the same page and click on: session2 -> Object Oriented Intro.ipynb. Then right click on the "Raw" button (upper right), and "save link as"/"download link" (or whatever is similar for your browser). Or just use this link (its the same thing).
Once you've got it locally, open a terminal, cd into that directory, and run the command jupyter notebook.
A point in 2D space has two coordinates. A circle can be thought of as a point with a radius. This is a common use of inheritance: we'll need to define a Point class to store the x and y positions, and a Circle class (which is a subclass of Point), which also has a radius.
Along the way we'll also implement methods to draw the objects (using the matplotlib plotting package). Don't worry about the details of anything that starts with plt, just know that it's calling matploltlib to actually make the plots.
# This cell has some stuff needed to make the "draw" methods work. It's not
# important to understand them for now, but you do need to run them to get
# things to work in the notebook
# This makes notebooks render plots inside the notebook
%matplotlib inline
# And these are imports from the "matplotlib" plotting package (more on that in a future session)
from matplotlib import pyplot as plt
class Point:
def __init__(self, x, y):
# this is the method that gets called when you create a new Point
self.x = x
self.y = y
def draw(self):
plt.scatter([self.x], [self.y])
# if you're on python 2.x, the top line should be:
#class Point(object):
p = Point(1, 2)
p.draw() # You can just call draw, and not worry at all about *how* the drawing happens
class Circle(Point): # This means "define a class Circle that is a subclass of Point"
def __init__(self, x, y, radius):
super().__init__(x,y)
# can also be this (works in py 2.x):
#Point.__init__(self, x, y)
self.radius = radius
def draw(self): # this method "overrides" the `draw` method of Point
super(Circle, self).draw()
# can also be this (works in py 2.x):
#Point.draw(self)
# note for people not familiar with matplotlib: alpha keyword below sets transparency
circle_patch = plt.Circle((self.x, self.y), self.radius, alpha=.5)
plt.gca().add_patch(circle_patch)
def compute_area(self):
from math import pi
return pi * self.radius**2
pointishes = [Point(0.5, 2), Circle(2, 3, 1.2), Point(3.5, 4)]
for obj in pointishes:
obj.draw() # again, all I need to know is that they "can be drawn"
print(pointishes[1].compute_area())
This is a more complex class heirarchy, which includes demonstrating multiple inheritance by way of the "diamond" pattern illustrated below. Also a fairly cynical view of how our science gets done.
<img src="AstronomerClassHeirarchy.svg" width=40%>
class Astronomer:
def __init__(self):
self._science_done = []
def do_science(self):
raise NotImplementedError("What, you think astronomers are all alike? Pick something more specific.")
@property
def cv(self):
return '\n'.join(self._science_done)
class Observer(Astronomer):
def __init__(self, favorite_targets):
self.favorite_targets= favorite_targets
super().__init__()
def do_science(self):
target, success = self.observe()
if success:
self._science_done.append('Sucessfully observed ' + target)
else:
self._science_done.append('Failed to observe ' + target)
success_rate = .1 # TAC+weather if you're ground based, maybe?
def observe(self):
import random
target = self.favorite_targets[random.randint(0, len(self.favorite_targets)-1)]
success = random.random() < self.success_rate
return target, success
class Theorist(Astronomer):
def do_science(self):
theory = self.make_theory()
self._science_done.append('Foundational work on theory of ' + theory)
def make_theory(self):
import random
# Eh, we just make it all up anyway
theory = chr(random.randint(97, 97+25)).upper()
for _ in range(random.randint(4, 10)):
theory += chr(random.randint(97, 97+25))
return theory
class Hybrid(Observer, Theorist):
def __init__(self, fraction_theorist, favorite_targets):
super().__init__(favorite_targets)
self.last_theory = None
self.fraction_theorist = fraction_theorist
def do_science(self):
import random
if self.last_theory is None or (random.random() < self.fraction_theorist):
self.last_theory = self.make_theory()
else:
target, success = self.observe()
if success:
self._science_done.append('Used {} to prove theory {}'.format(target, self.last_theory))
else:
self._science_done.append('Used {} to falsify theory {}'.format(target, self.last_theory))
iva_momcheva = Observer(['AEGIS', 'COSMOS', 'GOODS', 'UDS', 'Clg J0218.3-0510', 'assorted fancy lens clusters'])
erik_tollerud = Hybrid(.4, ['an M31 satellite', 'a Milky Way satellite', 'an isolated LG Dwarf', 'a Local Volume Dwarf'])
for _ in range(20):
iva_momcheva.do_science()
erik_tollerud.do_science()
print("Erik's CV:")
print(erik_tollerud.cv)
print("\nIva's CV:")
print(iva_momcheva.cv)
fritz_zwicky = Theorist()
for _ in range(150):
fritz_zwicky.do_science()
print("Zwicky's CV:")
print(fritz_zwicky.cv)
astropy.coordinates: http://docs.astropy.org/en/stable/coordinates/index.html#class-inheritance-diagram, and the documentation for how to make your own custom frames: http://docs.astropy.org/en/stable/coordinates/frames.html#defining-a-new-framesuper() works (you'll probably have some surprises!).